package com.xexun.gpstracker.component;

import java.lang.ref.WeakReference;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

import android.content.Context;
import android.location.Location;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;

import com.amap.api.location.AMapLocation;
import com.xexun.gpstracker.AppContext;
import com.xexun.gpstracker.MainActivity;
import com.xexun.gpstracker.channel.R;
import com.xexun.gpstracker.baby.activity.ChatActivity;
import com.xexun.gpstracker.common.Constants;
import com.xexun.gpstracker.pojo.ChatMsg;
import com.xexun.gpstracker.service.CommunicationService;
import com.xexun.gpstracker.util.*;
import com.xexun.gpstracker.vo.*;
import org.json.JSONException;
import org.json.JSONObject;

import static com.xexun.gpstracker.MainActivity.getActivity;

/**
 * Created by HH
 * Date: 2015/7/22 0022
 * Time: 上午 9:34
 */
public class MessageSender {

    private static Context mContext;
    private MessageSenderThread senderThread;
    private Thread heartBeatThread;
    private static MessageSender sender;
    private SocketClient client;
    private Map<String, Long> nameMap = new ConcurrentHashMap<>();
    private Map<String, byte[]> cache = new ConcurrentHashMap<>();
    private Map<Long, Long> countMap = new ConcurrentHashMap<>();
    private Boolean handlerRunning = false;

    private int packSerial = Constants.SERIAL_BEGIN;
    private static MessageSenderHandler msgSenderHandler;
    private Map<String, Integer> reSendCountMap = new ConcurrentHashMap<>();

    private final static int RESEND = 0;
    private final static int COUNT_DOWN = 1;

    private MessageSender() {

    }

    public static MessageSender getInstance() {
        if (sender == null) {
            synchronized (MessageSender.class) {
                if (sender == null) {
                    sender = new MessageSender();
                }
            }
        }
        return sender;
    }

    public static MessageSender getInstance(Context context) {
        if (sender == null) {
            synchronized (MessageSender.class) {
                if (sender == null) {
                    sender = new MessageSender();
                    mContext = context;
                    msgSenderHandler = new MessageSenderHandler(new WeakReference<>(sender));
                }
            }
        }
        return sender;
    }

    public synchronized void start(SocketClient client) {
        this.client = client;
        if (senderThread != null) {
            senderThread.setClient(client);
        } else {
            senderThread = new MessageSenderThread(client);
            senderThread.start();
        }
    }

    /**
     * 启动发送心跳包线程
     *
     * @return
     */
    public boolean sendHeartBeat() {
        if (heartBeatThread == null) {
            synchronized (MessageSender.class) {
                if (heartBeatThread == null) {
                    heartBeatThread = new Thread("Heart Beat Thread") {
                        @Override
                        public void run() {
                            int count = 1;
                            while (true) {
                                try {
                                    Date date = new Date();
                                    int second = date.getSeconds();
                                    if(Constants.FLAVOR == FlavorType.PHONELOC) {
                                        int nowMin = date.getHours() * 60 + date.getMinutes();
                                        if (nowMin % Constants.loctionInterval == 0) {
                                            Log.i(Constants.TAG, "定时定位时间到");
                                            MainActivity.getLocationEx();
                                        }
                                    }
                                    if (!Utils.isNetworkConnected(AppContext.getContext()) || client == null || !client.isConnected()) {
                                        CommunicationService cs = CommunicationService.get();
                                        if (cs != null) {
                                            cs.connect();
                                        }
                                        synchronized (this) {
                                            this.wait(Constants.HEART_BEAT_INTERVAL);
                                        }
                                        continue;
                                    }
                                    count++;
                                    if(count == 2) {
                                        count = 0;
                                        sendHeartBeatSingle();
                                    }

                                    synchronized (this) {
                                        this.wait(Constants.HEART_BEAT_INTERVAL - second*1000);
                                    }
                                } catch (InterruptedException ie) {
                                    Log.i(MessageSender.class.getName(), "心跳线程已结束！");
                                    return;
                                } catch (Exception e) {
                                    Log.e(Constants.TAG, "发送心跳包错误", e);
                                }
                            }
                        }
                    };
                    heartBeatThread.start();
                }
            }
        }
        return true;
    }

    /**
     * 发送单个心跳
     */
    public void sendHeartBeatSingle() {
        byte[] bytes = new byte[13];
        ByteUtil.setBytes(bytes, 0, 2, (short) 0xFFFF);
        ByteUtil.setBytes(bytes, 2, 1, (byte) 0x01);
        ByteUtil.setBytesByHex(bytes, 3, 8, Session.getInstance().getImei());
        ByteUtil.setBytes(bytes, 11, 2, (short) 0xFFFF);
        senderThread.addBytes(bytes);
        senderThread.notifyBytes();
    }

    /**
     * 发送登录包
     *
     * @param imei
     * @return
     */

    public boolean sendLogin(String imei) {
        try {
            byte[] bytes = new byte[22];
            ByteUtil.setBytes(bytes, 0, 2, (short) 0xFFFF);
            ByteUtil.setBytes(bytes, 2, 1, (byte) 0x00);
            ByteUtil.setBytesByHex(bytes, 3, 8, imei);
            ByteUtil.setBytesByHex2(bytes, 11, 8, IMEI.getFirstIMEI(mContext));
            Log.i(Constants.TAG,"mContext:"+IMEI.getFirstIMEI(mContext));
            String[] locales = mContext.getResources().getStringArray(R.array.locale_lang);
            Log.e("locale",Locale.getDefault().toString());
            ByteUtil.setBytes(bytes, 19, 1, locales[2].split("_")[0].equals(Locale.getDefault().toString().split("_")[0]) ? (byte) 0x00 : (byte) 0x01);
            ByteUtil.setBytes(bytes, 20, 2, (short) 0xFFFF);
            senderThread.addBytesAtFirst(bytes);
            senderThread.notifyBytes();
            sendHeartBeat();
            return true;
        } catch (Exception e) {
            Log.e(Constants.TAG, "发送登录包错误", e);
            return false;
        }
    }

    /**
     * 不要直接调用本类，应该使用 AppContext.getEventBus().post(chatMsg, Constants.EVENT_TAG_CHAT_SEND); 来发送
     *
     * @param chatMsg
     * @return
     */
    public boolean sendMessage(ChatMsg chatMsg) {
        if (chatMsg == null) {
            return true;
        }
        if (senderThread == null) {
            senderThread = new MessageSenderThread(client);
            senderThread.start();
        }
        Looper.prepare();
        byte[] bytes;
        byte[] bodyBytes;
        if (isTxtType(chatMsg)) {
            bodyBytes = chatMsg.getContent().getBytes();
        } else {
            bodyBytes = FileUtils.readFileAsBytes(AppContext.getContext(), chatMsg.getLocalUrl());
        }

        Session session = Session.getInstance();
        String currUserImei = CommUtil.isNotBlank(session.getImei()) ? session.getImei() : session.getUser().getImei();
        if (CommUtil.isBlank(currUserImei) || currUserImei.length() != 15) {
            CommUtil.showMsgShort(mContext.getString(R.string.imei_error));
            return false;
        }
        String name = chatMsg.getImei() + currUserImei + CommUtil.generateUniqueId(chatMsg.getId(), 14);
        nameMap.put(name, chatMsg.getId());
        String ext = getMsgExt(chatMsg);
        int extByteLen = ext.getBytes().length;
        int pgLen = Constants.PACKAGE_BODY_LENGTH;
        int byteLeft = bodyBytes.length % pgLen;
        int packs = byteLeft == 0 ? bodyBytes.length / pgLen : bodyBytes.length / pgLen + 1;
        int termFlag = TermType.APP.getType().intValue() == chatMsg.getTermType().intValue() ? 1 : 0;
        for (int i = 0; i < packs; i++) {
            int start = i * pgLen;
            if (i < packs - 1 || byteLeft == 0) {
                bytes = new byte[41 + pgLen + extByteLen];
                System.arraycopy(bodyBytes, start, bytes, 39 + extByteLen, pgLen);//内容体
                ByteUtil.setBytes(bytes, 7, 2, (short) pgLen);//内容体长度
                if(isJsonType(chatMsg)) {
                    byte flag = (byte) (((termFlag << 1) + 1) & 0xFF);
                    flag |= 0x10;
                    ByteUtil.setBytes(bytes, 9, 1, (byte) flag);//类型识别标识:APP 连续包
                } else {
                    ByteUtil.setBytes(bytes, 39 + extByteLen + pgLen, 2, (short) 0xFFFF);//结束
                }
            } else {
                bytes = new byte[41 + byteLeft + extByteLen];
                System.arraycopy(bodyBytes, start, bytes, 39 + extByteLen, byteLeft);//内容体
                ByteUtil.setBytes(bytes, 7, 2, (short) byteLeft & 0xFFFF);//内容体长度
                if(isJsonType(chatMsg)) {
                    byte flag = (byte) (((termFlag << 1)) & 0xFF);
                    flag |= 0x10;
                    ByteUtil.setBytes(bytes, 9, 1, (byte) flag);//类型识别标识:APP 连续包
                } else {
                    ByteUtil.setBytes(bytes, 9, 1, (byte) ((termFlag << 1) & 0xFF));//类型识别标识:APP 连续包
                }
                ByteUtil.setBytes(bytes, 39 + extByteLen + byteLeft, 2, (short) 0xFFFF);//结束
            }
            ByteUtil.setBytes(bytes, 0, 2, (short) 0xFFFF);//头
            ByteUtil.setBytes(bytes, 2, 1, (byte) 0x02);//控制域
            ByteUtil.setBytes(bytes, 3, 4, bodyBytes.length);//总长度

            int pkgNo;
            if (chatMsg.getType() == ContType.CMD.getType()) {
                if (packSerial >= 120) {
                    packSerial = Constants.SERIAL_BEGIN;
                }
                pkgNo = packSerial++;
            } else {
                pkgNo = i + 1;
            }
            ByteUtil.setBytes(bytes, 10, 1, (byte) (pkgNo & 0xFF));//包序号
            ByteUtil.setBytes(bytes, 11, 1, (byte) (chatMsg.getType() & 0xFF));//数据的格式
            ByteUtil.setBytes(bytes, 12, 4, start);//起始位置
            ByteUtil.setBytesByHex(bytes, 16, 22, name);//文件名
            ByteUtil.setBytes(bytes, 38, 1, (byte) (extByteLen & 0xFF));//后缀长度
            if (extByteLen > 0) {
                ByteUtil.setBytes(bytes, 39, extByteLen, ext);//后缀
            }
            String key = name + "_" + pkgNo;
            cache.put(key, bytes);
            reSendCountMap.put(key, 0);
            if (msgSenderHandler == null) {
                msgSenderHandler = new MessageSenderHandler(new WeakReference<>(sender));
            }
            msgSenderHandler.sendEmptyMessageDelayed(RESEND, Constants.RESEND_INTERVAL);

            if (senderThread != null) {
                senderThread.addBytes(bytes);
                senderThread.notifyBytes();
            }

            //聊天界面显示进度
            if (chatMsg.getId() != null && chatMsg.getId() > 0) {
                SenderMsgProgress progress = new SenderMsgProgress();
                progress.setId(chatMsg.getId());
                progress.setProgress((i + 1) / (double) packs * 100);
                AppContext.getEventBus().post(progress, Constants.EVENT_TAG_CHAT_PROGRESS);
            }
        }

        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            //
        }
        if (chatMsg.getId() != null && chatMsg.getId() > 0) {
            //发送完成后再发送一次，以显示100%进度，并且重置进度
            SenderMsgProgress progress = new SenderMsgProgress();
            progress.setId(chatMsg.getId());
            progress.setProgress(-1D);
            AppContext.getEventBus().post(progress, Constants.EVENT_TAG_CHAT_PROGRESS);

            countMap.put(chatMsg.getId(), Constants.MSG_TIMEOUT);
            synchronized (handlerRunning) {
                if (!handlerRunning) {
                    msgSenderHandler.sendEmptyMessageDelayed(COUNT_DOWN, 1000);
                    handlerRunning = true;
                }
            }
        }
        return true;
    }

    private boolean isTxtType(ChatMsg chatMsg) {
        return chatMsg.getType() == ContType.TXT.getType()
                || chatMsg.getType() == ContType.CMD.getType()
                || chatMsg.getType() == ContType.JSON.getType();
    }

    private boolean isJsonType(ChatMsg chatMsg) {
        return chatMsg.getType() == ContType.JSON.getType();
    }

    private String getMsgExt(ChatMsg chatMsg) {
        if (chatMsg.getType() == ContType.TXT.getType()) {
            return "txt";
        } else if (chatMsg.getType() == ContType.CMD.getType()) {
            return "pos";
        } else if(chatMsg.getType() == ContType.JSON.getType()) {
            return "JSON";
        } else {
            return CommUtil.isNotBlank(chatMsg.getLocalUrl()) && chatMsg.getLocalUrl().contains(".") ?
                    chatMsg.getLocalUrl().substring(chatMsg.getLocalUrl().lastIndexOf('.') + 1, chatMsg.getLocalUrl().length()) : "";
        }
    }

    private String formatedDate() {
        SimpleDateFormat fmt = new SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault());
        return fmt.format(new Date());
    }

    /**
     * 接收到服务器回复包，分析哪些包需要重传
     *
     * @param name
     * @param bts
     */
    public void reSendPackage(String name, byte[] bts) {
        boolean isSuccess = true;
        for (int i = 0; i < bts.length; i++) {
            for (int j = 0; j < 8; j++) {
                int val = bts[i] >>> (8 - j - 1) & 0x1;
                if (val > 0) {
                    isSuccess = false;
                    int pkgNo = i * 8 + j;
                    byte[] bytes = cache.get(name + "_" + pkgNo);
                    if (bytes != null && bytes.length > 0) {
                        senderThread.addBytes(bytes);
                    }
                }
            }
        }
        if (!isSuccess) {
            //如果没有完全成功，则重发后重新开始计时
            senderThread.notifyBytes();
            Long id = nameMap.get(name);
            if (countMap.containsKey(id)) {
                countMap.put(id, Constants.MSG_TIMEOUT);
            }
        } else {
            markMsgSuccess(name);
        }
        clearPackage(name);
    }

    public void notifyBytesSuccess(String name) {
        markMsgSuccess(name);
        clearPackage(name);
    }

    public synchronized void markMsgSuccess(String name) {
        Long id = nameMap.get(name);
        if (id != null && id > 0) {
            //如果成功了，标记该消息为成功，并移除倒计时
            try {
                AppContext.db.execNonQuery("update chat_msg set succ=1 where id=" + id);
                ChatMsg chatMsg = AppContext.db.findById(ChatMsg.class, id);
                AppContext.getEventBus().post(chatMsg, Constants.EVENT_TAG_REFRESH_CHAT_MSG);
            } catch (Exception e) {
                Log.e(Constants.TAG, "更新消息状态失败", e);
            }
            countMap.remove(id);
        }
        clearReSendPackage(name);
    }

    public void clearPackage(String name) {
        synchronized (cache) {
            Iterator<Map.Entry<String, byte[]>> it = cache.entrySet().iterator();
            while (it.hasNext()) {
                if (it.next().getKey().startsWith(name)) {
                    it.remove();
                }
            }
        }
    }

    public void clearReSendPackage(String name) {
        synchronized (reSendCountMap) {
            Iterator<Map.Entry<String, Integer>> it = reSendCountMap.entrySet().iterator();
            while (it.hasNext()) {
                if (it.next().getKey().startsWith(name)) {
                    it.remove();
                }
            }
        }
    }

    public void close() {
        if (senderThread != null) {
            senderThread.interrupt();
            senderThread = null;
        }
        if (heartBeatThread != null) {
            heartBeatThread.interrupt();
            heartBeatThread = null;
        }
        if (client != null) {
            client.close();
            client = null;
        }
        msgSenderHandler = null;
    }

    /**
     * 收到完整消息后，回传接收包的成功状态
     *
     * @param name
     * @param succPkg
     * @param serial
     * @param ext
     * @param type
     */
    public void responseServer(String name, Set<Integer> succPkg, int serial, String ext, int type) {
        byte[] respBytes = new byte[]{0x00, 0x00, 0x00, 0x00};
        //FIXME
//        if (succPkg != null) {
//            for (int i = 0; i < respBytes.length; i++) {
//                for (int j = 0; j < 8; j++) {
//                    int idx = i * 8 + j;
//                    if (idx < serial && !succPkg.contains(idx + 1)) {
//                        respBytes[i] += 1 << (8 - j - 1);
//                    }
//                }
//            }
//        }
        int extLen = ext.getBytes().length;
        byte[] respBody = new byte[29 + respBytes.length + extLen];
        ByteUtil.setBytes(respBody, 0, 2, (short) 0xFFFF);
        ByteUtil.setBytes(respBody, 2, 1, (byte) 0x12);
        ByteUtil.setBytesByHex(respBody, 3, 22, name);
        ByteUtil.setBytes(respBody, 25, respBytes.length, respBytes);
        ByteUtil.setBytes(respBody, 25 + respBytes.length, 1, extLen);
        ByteUtil.setBytes(respBody, 26 + respBytes.length, extLen, ext);
        ByteUtil.setBytes(respBody, 26 + respBytes.length + extLen, 1, getRespCode(type));
        ByteUtil.setBytes(respBody, 27 + respBytes.length + extLen, 2, (short) 0xFFFF);
        senderThread.addBytes(respBody);
        senderThread.notifyBytes();
    }

    /**
     * @param type
     * @return 约定见协议
     */
    private int getRespCode(int type) {
        int ret = 0x00;
        if (ContType.FENCE.getType() == type) {
            ret = 0x01;
        } else if (ContType.TERM_STATUS.getType() == type) {
            ret = 0x02;
        }
        return ret;
    }

    public void notifySenderThread() {
        senderThread.notifyBytes();
    }

    public void notifyAllFailed() {
        if (countMap == null || countMap.isEmpty()) {
            return;
        }
        Iterator<Map.Entry<Long, Long>> it = countMap.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry<Long, Long> entry = it.next();
            updateMsgState(entry.getKey());
            it.remove();
        }
    }

    private void updateMsgState(long id) {
        try {
            AppContext.db.execNonQuery("update chat_msg set succ=0 where id=" + id);
            ChatActivity chatActivity = ChatActivity.getChatActivity();
            if (chatActivity != null) {
                Message msg = chatActivity.getHandler().obtainMessage(ChatActivity.UPDATE_CHAT_FAILED);
                msg.obj = id;
                chatActivity.getHandler().sendMessage(msg);
            }
        } catch (Exception e) {
            Log.e(Constants.TAG, "更新消息状态失败", e);
        }
    }

    public class SenderMsgProgress {
        private Long id;
        private Double progress;

        public Long getId() {
            return id;
        }

        public void setId(Long id) {
            this.id = id;
        }

        public Double getProgress() {
            return progress;
        }

        public void setProgress(Double progress) {
            this.progress = progress;
        }
    }

    private static class MessageSenderHandler extends Handler {
        private MessageSender mSender;

        public MessageSenderHandler(WeakReference<MessageSender> mSender) {
            this.mSender = mSender.get();
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case RESEND:
                    Iterator<Map.Entry<String, Integer>> it = mSender.reSendCountMap.entrySet().iterator();
                    while (it.hasNext()) {
                        Map.Entry<String, Integer> entry = it.next();
                        int ct = entry.getValue();
                        if (ct < Constants.MAX_RESEND_TIMES) {
                            byte[] bytes = mSender.cache.get(entry.getKey());
                            if (bytes != null && bytes.length > 0) {
                                Log.d(MessageSender.class.getName(), "重发包：" + entry.getKey());
                                if (mSender.senderThread != null) {
                                    mSender.senderThread.addBytes(bytes);
                                    mSender.senderThread.notifyBytes();
                                }
                            }
                            entry.setValue(ct + 1);
                        } else {
                            it.remove();
                        }
                    }
                    if (!mSender.reSendCountMap.isEmpty()) {
                        sendEmptyMessageDelayed(0, Constants.RESEND_INTERVAL);
                    }
                    break;
                case COUNT_DOWN:
                    if (mSender.countMap.isEmpty()) {
                        mSender.handlerRunning = false;
                        break;
                    }
                    Iterator<Map.Entry<Long, Long>> iterator = mSender.countMap.entrySet().iterator();
                    while (iterator.hasNext()) {
                        Map.Entry<Long, Long> entry = iterator.next();
                        long timeLeft = entry.getValue() - 1;
                        if (timeLeft <= 0) {
                            //倒计时完成，说明消息超时，标记失败
                            mSender.updateMsgState(entry.getKey());
                            iterator.remove();
                        } else {
                            entry.setValue(timeLeft);
                        }
                    }
                    sendEmptyMessageDelayed(COUNT_DOWN, 1000);
                    break;
            }
        }
    }
}
